<template>
  <div class="container">
    <div id="map-container">
      <div id="point-popup" ref="pointPopupRef">
        <PointPopup :coordinate="curCoordinate" />
      </div>
    </div>
    <div class="custom-control">
      <div class="control">
        <el-button type="primary" @click="startAnimation">开始</el-button>
        <el-button type="primary" @click="stopAnimation">停止</el-button>
        <div class="label">速度</div>
        <el-slider class="w-300" v-model="speed" :min="100" :max="10000" />
        <el-button style="margin-left: 15px;" type="primary" @click="curSpeedChange()">倍速{{ curSpeed }}X</el-button>
      </div>
      <div class="time">
        <el-slider v-model="lastRouteIndex" :min="0" :max="lineRoutesAll.length" />
      </div>
    </div>
  </div>
</template>
<script setup lang='ts'>
import { onMounted, ref, watch } from 'vue';
import 'ol/ol.css'
import { Map, View, Feature, Overlay } from 'ol'
import VectorSource from 'ol/source/Vector'
import { Vector as VectorLayer } from 'ol/layer'
import { Style, Fill as StyleFill, Stroke as StyleStroke, Circle as StyleCircle, Icon as StyleIcon } from 'ol/style'
import { Point as GeomPoint, LineString as GeomLineString, Geometry } from 'ol/geom'
import { getDistance } from "ol/sphere";
import { getVectorContext } from "ol/render";

import { coordinateTransform, getImageUrl } from '@/utils'
import { useTianTileLayer, projection } from '@/hooks'
import { TiandiTileLayerConfigEnum } from '@/enum'
import RenderEvent from 'ol/render/Event';
import PointPopup from './popup/index.vue'
import { CENTER_BLHX_LONG_LAT } from '@/constant'

let map: Map,
routeSource: VectorSource<Feature<Geometry>>,
routeLayer: VectorLayer<VectorSource<Feature<Geometry>>>,
lineRoutes: Array<Array<number>>,
routeGeometry: GeomLineString,
aeroGeometry: GeomPoint,
aeroFeature: Feature<GeomPoint>,
popupOverlay: Overlay;
const mStyles = Object.freeze({
  route: new Style({
    stroke: new StyleStroke({
      lineDash: [2, 6, 10],
      width: 4,
      color: [0, 255, 0, 1],
    }),
  }),
  marker: new Style({
    image: new StyleCircle({
      radius: 10,
      stroke: new StyleStroke({
        color: "#fff",
      }),
      fill: new StyleFill({
        color: "#3399CC",
      }),
    }),
  }),
  aeroMarker: new Style({
    image: new StyleIcon({
      rotation: 0,
      src: getImageUrl('aero.png'),
      // imgSize: [2, 5],
      scale: 0.3,
    })
  })
})

const speed = ref(2000)
const curSpeed = ref(1)
const lastRouteIndex = ref(0)
const lineRoutesAll = ref<Array<{ rotation?: number, coordinate: Array<number> }>>([])
const curCoordinate = ref<number[]>([])

let pointPopupRef = ref<HTMLDivElement>()

watch(speed, getRoutesAll)

onMounted(() => {
  initMap()
})

// 初始化地图
function initMap() {
  routeSource = new VectorSource({ wrapX: false });
  routeLayer = new VectorLayer({
    source: routeSource,
    style: feature => {
      return mStyles[(feature.get("type") as 'aeroMarker' | 'marker' | 'route')];
    }
  })
  map = new Map({
    target: 'map-container',
    layers: [
     ...useTianTileLayer(TiandiTileLayerConfigEnum.SL),
      routeLayer
    ],
    view: new View({
      center: CENTER_BLHX_LONG_LAT,
      projection: projection,
      zoom: 3,
      maxZoom: 17,
      minZoom: 1
    })
  })
  getCoordinate()
}

// 获取坐标
function getCoordinate() {
  const coordinates = [
    [170.168644,60.003816],
    [196.219216,59.930029],
    [216.373455,34.240125],
    [243.012741,32.261612]
  ]
  // lineRoutes = mydata
  lineRoutes = coordinates
  curCoordinate.value = lineRoutes[0]
  getRoutesAll()
  drawRoute()
}

// 分割路径点
function getRoutesAll() {
  lastRouteIndex.value = 0
  let _routesAll = [{
    coordinate: lineRoutes[0],
  }]
  for (let i = 0, len = lineRoutes.length; i < len - 1; i++) {
    const item = lineRoutes[i]
    const itemNext = lineRoutes[i + 1]
    const rotation = getRotation(item[0], item[1], itemNext[0], itemNext[1])
    let points = getCenterPoint(map, [item, itemNext], speed.value)
    points = points.map((item) => {
      return { rotation, coordinate: item}
    })
    _routesAll = [..._routesAll, ...points];
  }
  lineRoutesAll.value = _routesAll;
}

// 根据坐标获取弧度
function getRotation(lng1: number, lat1: number, lng2: number, lat2: number) {
  let rotation = Math.atan2(lng2 - lng1, lat2 - lat1);
  return rotation;
}

/**
 * 计算两点之间的中间点
 * @param {*} map
 * @param {Array} pointDoubleArray 二维数组坐标
 * @param {num} speed 每个点之间的距离
 */
function getCenterPoint(map: Map, pointDoubleArray: Array<number[]>, speed: number) {
  speed = speed || 50
  let twolength = formatLength(map, pointDoubleArray);
  let rate = twolength / speed; //比例 默认10m/点
  let step = Math.ceil(rate); //步数（向上取整）
  let arr = new Array(); //定义存储中间点数组
  let c1 = pointDoubleArray[0]; //头部点
  let c2 = pointDoubleArray[1]; //尾部点
  let x1 = c1[0], y1 = c1[1];
  let x2 = c2[0], y2 = c2[1];
  for (let i = 1; i < step; i++) {
    let coor = new Array(2);
    coor[0] = ((x2 - x1) * i) / rate + x1;
    coor[1] = ((y2 - y1) * i) / rate + y1;
    arr.push(coor); //此时arr为中间点的坐标
  }
  arr.push(c2);
  return arr;
}

// 计算两点坐标之间的距离
function formatLength(map: Map, pointArray: Array<number[]>) {
  let length = 0;
  if ((map.getView().getProjection() as any).code_ == "EPSG:4326") {
    for (let i = 0, ii = pointArray.length - 1; i < ii; ++i) {
      let c1 = pointArray[i];
      let c2 = pointArray[i + 1];

      length += getDistance(c1, c2);
    }
  } else if ((map.getView().getProjection() as any).code_ == "EPSG:3857") {
    for (let i = 0, ii = pointArray.length - 1; i < ii; ++i) {
      let c1 = pointArray[i];
      let c2 = pointArray[i + 1];
      // c1 = transform(c1, "EPSG:3857", "EPSG:4326");
      // c2 = transform(c2, "EPSG:3857", "EPSG:4326");
      c1 = coordinateTransform(c1, true);
      c2 = coordinateTransform(c2, true);
      length += getDistance(c1, c2);
    }
  }
  return length;
}

// 绘制轨迹
function drawRoute() {
  if (routeGeometry) {
    routeSource.clear();
  }
  routeGeometry = new GeomLineString(lineRoutes);
  let route = new Feature({
    type: "route",
    geometry: routeGeometry,
  });
  route.setStyle(new Style({
    stroke: new StyleStroke({
      color: '#4b59d8',
      width: 2
    })
  }))
  // 绘制点
  let opints = drawPoint();
  // 添加飞机
  let aero = drawAero();
  routeSource.addFeatures([route, ...opints, aero]);
  // 设置飞机第一次的角度
  aeroFeature.setStyle(new Style({
    image: new StyleIcon({
      anchor: [0.5, 0.5],
      rotation: lineRoutesAll.value[1].rotation,
      src: getImageUrl('aero.png'),
      scale: 0.3
    })
  }))
  addOverlay(routeGeometry.getFirstCoordinate())
  // 按轨迹边界缩放
  // mapFit();
}

// 画点
function drawPoint() {
  let iconFeatures: Array<Feature<GeomPoint>> = [];
  lineRoutes.forEach(item => {
    const Point = new Feature({
      type: "marker",
      geometry: new GeomPoint(item),
    });
    Point.setStyle(new Style({
      image: new StyleCircle({
        radius: 10,
        fill: new StyleFill({
          color: "blue",
        }),
      }),
    }))
    iconFeatures.push(Point);
  });
  return iconFeatures
}

// 飞机
function drawAero() {
  aeroGeometry = new GeomPoint(routeGeometry.getFirstCoordinate());
  aeroFeature = new Feature({
    type: "aeroMarker",
    geometry: aeroGeometry,
  });
  return aeroFeature;
}

function mapFit() {
  let view = map.getView();
  view.fit(routeGeometry, {
    padding: [120, 120, 120, 120],
  });
}

// 开始动画
function startAnimation() {
  if(!routeLayer || !aeroFeature) return
  routeLayer.on("postrender", moveFeature);
  aeroFeature.setGeometry(undefined);
}

// 停止动画
function stopAnimation() {
  if(!routeLayer || !aeroFeature) return
  aeroFeature.setGeometry(aeroGeometry);
  // 停止后 也设置一次飞机角度 让飞机朝向航迹方向
  aeroFeature.setStyle(new Style({
    image: new StyleIcon({
      anchor: [0.5, 0.5],
      rotation: lineRoutesAll.value[lastRouteIndex.value].rotation || lineRoutesAll.value[lastRouteIndex.value + 1].rotation || 0,
      src: getImageUrl('aero.png'),
      scale: 0.3
    })
  }))
  routeLayer.un("postrender", moveFeature);
}

// 移动动画
function moveFeature(event: RenderEvent) {
  const len = lineRoutesAll.value.length;
  if(curSpeed.value == 2) {
    lastRouteIndex.value = lastRouteIndex.value + 1
  } else if(curSpeed.value == 3) {
    lastRouteIndex.value = lastRouteIndex.value + 2
  }
  if (lastRouteIndex.value < len - 1) {
    lastRouteIndex.value++;
  } else {
    lastRouteIndex.value = 0;
  }
  const current = lineRoutesAll.value[lastRouteIndex.value];
  if(lineRoutes.findIndex(coor => current.coordinate[0] == coor[0] && current.coordinate[1] == coor[1]) >= 0) {
    // 当沿着轨迹运行到坐标点时 进来
    console.log(current.coordinate, '当前坐标--->>')
  }
  aeroGeometry.setCoordinates(current.coordinate);
  popupOverlay.setPosition(current.coordinate);
  curCoordinate.value = current.coordinate;
  const vectorContext = getVectorContext(event);
  let _style = new Style({
    image: new StyleIcon({
      anchor: [0.5, 0.5],
      rotation: current.rotation,
      src: getImageUrl('aero.png'),
      scale: 0.3
    })
  })
  vectorContext.setStyle(_style)
  vectorContext.drawGeometry(aeroGeometry)
  map.render()
}

// 改变倍速
function curSpeedChange() {
  if (curSpeed.value == 3) {
    curSpeed.value = 1
  } else {
    curSpeed.value++
  }
}

// 添加popup弹出
function addOverlay(coordinates: Array<number>) {
  popupOverlay = new Overlay({
    //要转换成overlay的HTML元素
    element: pointPopupRef.value,
    //当前窗口可见
    autoPan: false,
    //Popup放置的位置
    positioning: 'bottom-center',
    //是否应该停止事件传播到地图窗口
    stopEvent: false
  });
  map.addOverlay(popupOverlay);
  popupOverlay.setPosition(coordinates)
}

</script>
<style scoped>
.container {
  width: 100%;
  height: 100%;
}

.custom-control {
  width: 100%;
  height: 80px;
  padding: 5px;
  box-sizing: border-box;
}

.custom-control .control {
  width: 100%;
  height: 50%;
  display: flex;
  align-items: center;
}

.custom-control .control .label {
  color: #000;
  font-size: 14px;
  margin: 0 15px;
}

.custom-control .time {
  width: 100%;
  height: 50%;
  display: flex;
  align-items: center;
}

#map-container {
  width: 100%;
  height: calc(100% - 80px);
  position: relative;
  z-index: 9;
}

#point-popup {
  width: 280px;
  height: 200px;
  position: absolute;
  top: 10px;
  right: 5px;
  box-sizing: border-box;
  z-index: 0;
}

.w-300 {
  width: 300px;
}
</style>